<?php

declare(strict_types=1);
/**
 * TechnicalSupport: Shin
 * Email: shin_php@163.com
 * ReformatDate: 2022-09-22 04:10:33
 * DESC: The Relentless Pursuit of Perfection.
 */
namespace Common\Kernel\Model;

use Hyperf\Database\Query\Builder;
use Hyperf\Database\Query\Grammars\Grammar as BaseGrammar;
use function Hyperf\Collection\collect;

class Grammar extends BaseGrammar
{
    /**
     * Compile an insert statement into SQL.
     */
    public function compileInsertOrUpdate(Builder $query, array $values): string
    {
        // Essentially we will force every insert to be treated as a batch insert which
        // simply makes creating the SQL easier for us since we can utilize the same
        // basic routine regardless of an amount of records given to us to insert.
        $table = "`{$this->tablePrefix}{$query->from}`";

        if (! is_array(reset($values))) {
            $values = [$values];
        }
        $fields = array_keys(reset($values));
        $columns = [];

        $updateColumns = [];

        foreach ($fields as $field) {
            $updateColumns[] = "`{$field}`=values(`{$field}`)";
            $columns[] = "`{$field}`";
        }
        $updateColumns = implode(',', $updateColumns);
        $columns = implode(',', $columns);
        // We need to build a list of parameter place-holders of values that are bound
        // to the query. Each insert should have the exact same amount of parameter
        // bindings so we will loop through the record and parameterize them all.
        $parameters = collect($values)->map(function ($record) {
            return '(' . $this->parameterize($record) . ')';
        })->implode(', ');

        return "insert into {$table} ({$columns}) values {$parameters}  on duplicate key update {$updateColumns}";
    }

    /**
     * Compile an insert statement into SQL.
     * 
     * @param Builder $query
     * @param array $multipleData
     * @param $key
     * @return string
     * @throws \Exception
     */
    public function compileBatchUpdate(Builder $query, array $multipleData, $key = 'id'): string
    {
        $table = "`{$this->tablePrefix}{$query->from}`";

        $firstRow = current($multipleData);

        $updateColumn = array_keys($firstRow);

        // 默认以id为条件更新
        $referenceColumn = isset($firstRow[$key]) ? $key : 'id';

        if (! isset($firstRow[$referenceColumn])) {
            throw new \Exception($referenceColumn . ' key not found!');
        }

        unset($updateColumn[0]);
        $updateSql = 'UPDATE ' . $table . ' SET ';
        $sets = [];
        foreach ($updateColumn as $uColumn) {
            $setSql = '`' . $uColumn . '` = CASE ';
            foreach ($multipleData as $data) {
                $setSql .= 'WHEN `' . $referenceColumn . '` = ? THEN ? ';
            }
            $setSql .= 'ELSE `' . $uColumn . '` END ';
            $sets[] = $setSql;
        }

        $updateSql .= implode(', ', $sets);

        $whereIn = collect($multipleData)->pluck($referenceColumn)->values()->all();

        $whereIn = rtrim(str_repeat('?,', count($whereIn)), ',');

        return rtrim($updateSql, ', ') . ' WHERE `' . $referenceColumn . '` IN (' . $whereIn . ')';
    }
}
